/**
 * @file crypto_t1.c
 * @brief
 *
 * @version V0.1
 * @author Hezh
 * @date 2012-07-24
 */
#include "crypto.h"
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include "config.h"

#define DEVICE_DES      "/dev/des"
#define DEVICE_AES128   "/dev/aes"
#define DEVICE_RSA      "/dev/rsa"
#define DEVICE_SHA256   "/dev/sha256"
#define DEVICE_RNG      "/dev/rng"

static int fd_sha256 = -1;
static int fd_des = -1;
static int fd_aes128 = -1;
static int fd_rsa = -1;
static int fd_rng = -1;

/* ------------------------Random------------------------*/
uint8_t rng_get_byte()
{
        if(fd_rng < 0){
                if ((fd_rng = open(DEVICE_RNG, O_RDWR|O_NONBLOCK)) <  0)
                        return -1;
        }
        uint8_t rng;
        read(fd_rng, &rng, 1);

        return rng;
}

int rng_get_string(uint8_t * buf, uint32_t len)
{		
		if( buf == NULL )
			return -1;
		
        if (buf && len) {
                if(fd_rng < 0){
                        if((fd_rng = open(DEVICE_RNG, O_RDWR|O_NONBLOCK)) <  0){
                                return -2;
                        }
                }
                read(fd_rng, buf, len);
        }

		return 0;
}
/* ------------------------ SHA256 Digest ------------------------*/
int sha2_starts(int is224)
{
        if(is224)		/*Only support sha256*/
                return -1;

        if(fd_sha256 < 0){
                if ((fd_sha256 = open( DEVICE_SHA256, O_RDWR|O_NONBLOCK)) <  0)
                        return -1;
        }
        ioctl(fd_sha256, IOCTL_CRYPTO_START, NULL);
        return 0;
}
void sha2_update(uint8_t * input, uint32_t length)
{
	if( input == NULL )
        	return;
		
        write(fd_sha256, input, length);        
}
void sha2_finish(uint8_t digest[32])
{
        if( digest == NULL )
                return;
        read(fd_sha256, digest, 32);
        ioctl(fd_sha256, IOCTL_CRYPTO_STOP, NULL);
}

/* ------------------------ DES Crypto ------------------------*/
static uint8_t des3Key[24];
static int des3_decrypt = 0;
#define DES_ENCRYPT			0
#define DES_DECRYPT			1

int deskey(uint8_t key[8], int decrypt)
{
		if( key == NULL )
			return -1;
		
        if( fd_des < 0 ){
                if ((fd_des = open(DEVICE_DES, O_RDWR|O_NONBLOCK)) <  0)
                        return -2;
        }
        ioctl(fd_des, IOCTL_CRYPTO_START, NULL);
        ioctl(fd_des, IOCTL_CRYPTO_SET_KEY, key );

        if(decrypt)
                ioctl(fd_des, IOCTL_CRYPTO_SET_DECRYPT, NULL);
        else
                ioctl(fd_des, IOCTL_CRYPTO_SET_ENCRYPT, NULL);

        return 0;
}

int des(uint8_t block[8], uint8_t output[8])
{	
        if( block == NULL || output == NULL )
        	return -1;

                
        ioctl(fd_des, IOCTL_CRYPTO_START, NULL);
        write(fd_des, block, 8);
        read(fd_des, output, 8);
        ioctl(fd_des, IOCTL_CRYPTO_STOP, NULL);

        return 0;
}
int des3key2(uint8_t key[16], int decrypt)
{
		if( key == NULL )
			return -1;
		
        des3_decrypt = decrypt;
        memcpy(des3Key, key, 16);
        memcpy(des3Key + 16, key, 8);

        return 0;
}
int des3key3(uint8_t key[24], int decrypt)
{
		if( key == NULL )
			return -1;
		
        des3_decrypt = decrypt;
        memcpy(des3Key, key, 24);

        return 0;
}

int des3(uint8_t block[8], uint8_t output[8])
{
		uint8_t tmp[8];
        int num = 1;
        uint8_t *pkey = des3Key;

		if( block == NULL || output == NULL )
			return -1;
		
        if(des3_decrypt == DES_DECRYPT){
                num = -1;
                pkey = des3Key + 16;
        }

        deskey(pkey, des3_decrypt);
        des(block, output);

        deskey(pkey + (8 * num), (des3_decrypt == DES_ENCRYPT));
        des(output, tmp);

        deskey(pkey + (16 * num), des3_decrypt);
        des(tmp, output);

        return 0;
}
/* ------------------------ AES128 Crypto ------------------------*/
int aeskey( uint8_t *key, uint32_t keysize, int decrypt)
{
	/*Only support aes128 */
	switch( keysize ) {
                case 128:
			break;
                default :
			return (-1);
        }

	if( key == NULL )
		return -1;

	if( fd_aes128 < 0 ){
		if ((fd_aes128 = open( DEVICE_AES128, O_RDWR|O_NONBLOCK)) <  0)
			return -1;
	}

	ioctl(fd_rsa, IOCTL_CRYPTO_START, NULL);
	ioctl(fd_aes128, IOCTL_CRYPTO_SET_KEY, key );

	if( decrypt )
		ioctl(fd_aes128, IOCTL_CRYPTO_SET_DECRYPT, NULL);
	else
		ioctl(fd_aes128, IOCTL_CRYPTO_SET_ENCRYPT, NULL);

	return 0;
}

int aes(int decrypt, uint8_t input[16], uint8_t output[16])
{
	if(input == NULL || output == NULL )
		return -1;

	ioctl(fd_rsa, IOCTL_CRYPTO_START, NULL);
	write(fd_aes128, input, 16);
	read(fd_aes128, output, 16);
	ioctl(fd_rsa, IOCTL_CRYPTO_STOP, NULL);

	return 0;
}

/* ------------------------	RSA Crypto ------------------------*/
int rsapublicfunc(uint8_t *output,      /* output block */
                uint32_t *outputLen,    /* length of output block */
                uint8_t *input,         /* input block */
                uint32_t inputLen,      /* length of input block */
                R_RSA_PUBLIC_KEY *publicKey /* RSA public key */)
{
	rsa_key_t pkey;
	int pos = 0;
	int ret;
	
	if(input == NULL || inputLen == 0 ||
		publicKey == NULL || output == NULL )
		return -1;

	if(fd_rsa < 0 ){
		if ((fd_rsa = open( DEVICE_RSA, O_RDWR|O_NONBLOCK)) <  0)
			return -2;
	}
	pkey.modlen = (publicKey->bits + 7) >> 3;
	pkey.mod = publicKey->modulus + (MAX_RSA_MODULUS_LEN - pkey.modlen);

	/* encrypt data cann't exceed modulus length */
	if( inputLen > pkey.modlen )
		return -3;

	if( (inputLen == pkey.modlen) && 
			(memcmp( input, pkey.mod, inputLen) >= 0))
		return -4; 
	
	while( *(publicKey->exponent + pos) == 0x00 ){
		pos++;
	}
	pkey.exp = publicKey->exponent + pos;
	pkey.explen = MAX_RSA_MODULUS_LEN - pos;
        
	ioctl(fd_rsa, IOCTL_CRYPTO_START, NULL);
	ioctl(fd_rsa, IOCTL_CRYPTO_SET_KEY, (void*)&pkey );
	write(fd_rsa, input, inputLen );
	ret = read(fd_rsa,output, pkey.modlen);
	if( outputLen != NULL )
		*outputLen =  ret;
        
	ioctl(fd_rsa, IOCTL_CRYPTO_STOP, NULL);
    return 0;
}




int rsaprivatefunc(uint8_t *output,     /* output block */
                uint32_t *outputLen,    /* length of output block */
                uint8_t *input,         /* input block */
                uint32_t inputLen,      /* length of input block */
                R_RSA_PRIVATE_KEY *privateKey /* RSA private key */)
{
	return rsapublicfunc(output, outputLen, input, inputLen, 
									(R_RSA_PUBLIC_KEY *)privateKey);
}
